package com.example.sentineldemo.controller;

import com.alibaba.csp.sentinel.*;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.example.sentineldemo.feign.HelloFeignService;
import com.example.sentineldemo.service.AsyncService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;

/**
 * @Package: com.example.sentineldemo.controller
 * @ClassName: HelloController
 * @Author: Think
 * @CreateTime: 2021/7/14 9:04
 * @Description:
 */
@Slf4j
@RestController
public class HelloController {

    /**
     * 抛出异常的方式定义资源
     *
     * @return
     */
    @GetMapping("/getHello")
    public String getHello() {
        // 使用限流规则，保护”业务逻辑“
        try (Entry entry = SphU.entry("getHello")) {
            // 正常的业务逻辑
            return "Hello";
        } catch (Exception e) {
            log.info("当前请求被限流了！", e);
            // 降级方案
            return "系统繁忙，请稍后再试！";
        }
    }

    /**
     * 返回布尔值方式定义资源
     *
     * @return
     */
    @GetMapping("/getHi")
    public String getHi() {
        if (SphO.entry("getHello")) {
            try {
                // 正常的业务逻辑
                return "Hi";
            } finally {
                SphO.exit();
            }
        } else {
            log.info("当前请求被限流了！");
            // 降级方案
            return "系统繁忙，请稍后再试！";
        }
    }

    @Autowired
    private AsyncService asyncService;

    /**
     * 异步调用方式定义资源
     */
    @GetMapping("/getAsync")
    public String getAsync() {
        AsyncEntry asyncEntry = null;
        try {
            asyncEntry = SphU.asyncEntry("getHello");
            // 正常的业务逻辑
            asyncService.asyncInvoke();
        } catch (BlockException e) {
            log.info("当前请求被限流了：", e);
            // 降级方案
            return "系统繁忙，请稍后再试！";
        } finally {
            if (asyncEntry != null) {
                asyncEntry.exit();
            }
        }
        return "OK";
    }

    /**
     * 使用注解额方式定义资源
     */
    @GetMapping("/getAnnotation")
    @SentinelResource(value = "getHello", blockHandler = "exceptionHandler")
    public String getAnnotation() {
        // 正常的业务逻辑
        return "annotation";
    }

    public String exceptionHandler(BlockException blockException) {
        // 降级方案
        log.info("当前请求被限流了：", blockException);
        return "系统繁忙，请稍后再试！";
    }

    @Autowired
    private HelloFeignService helloFeignService;

    /**
     * 整合Feign
     */
    @GetMapping("/getFeignHello")
    public String getFeignHello() {
        return helloFeignService.getHello();
    }

    /**
     * 系统自适应保护规则测试
     */
    @GetMapping("/getAdapt1")
    // 标识为入口资源，接受自适应保护
    @SentinelResource(entryType = EntryType.IN)
    public String getAdapt1(HttpServletRequest request) {
        log.info("from ip:{}", request.getRemoteAddr());
        return "adapt1";
    }

    @GetMapping("/getAdapt2")
    // 未标识为入口资源，但也属于入口流量
    public String getAdapt2() {
        return "adapt2";
    }

    /**
     * datasource指定为nacos测试
     *
     * @return
     */
    @GetMapping("/getNacos")
    public String getNacos() {
        return "nacos datasource！";
    }

    /**
     * 定义隔离规则
     *
     * @PostConstruct 当前类的构造函数执行之后执行该方法
     */
    @PostConstruct
    public void initFlowRule() {
        List<FlowRule> ruleList = new ArrayList<>();
        FlowRule rule = new FlowRule();
        // 设置资源名称
        rule.setResource("getHello");
        // 指定限流模式为QPS
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 指定QPS限流阈值
        rule.setCount(2);
        ruleList.add(rule);
        // 加载该规则
        FlowRuleManager.loadRules(ruleList);
    }

    /**
     * 使用Nacos数据源来管理规则
     */
/*    @PostConstruct
    public void initDataSource() {
        String remoteAddress = "192.168.31.17:8848";
        String groupId = "DEFAULT_GROUP";
        String dataId = "sentinel-nacos";
        ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(remoteAddress, groupId, dataId
                , source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
        }));
        FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
    }*/
}
